package com.shengqin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shengqin.common.pojo.CommonPage;
import com.shengqin.entity.PlanRelationClasses;
import com.shengqin.entity.PlanStudent;
import com.shengqin.entity.School;
import com.shengqin.entity.Student;
import com.shengqin.mapper.PlanStudentMapper;
import com.shengqin.pojo.plan.FreeTestImportModel;
import com.shengqin.pojo.plan.PlanQueryParam;
import com.shengqin.service.IPlanRelationClassesService;
import com.shengqin.service.IPlanStudentService;
import com.shengqin.service.ISchoolService;
import com.shengqin.service.IStudentService;
import com.shengqin.utils.GradeUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class PlanStudentServiceImpl extends ServiceImpl<PlanStudentMapper, PlanStudent> implements IPlanStudentService {

    @Resource
    private IStudentService studentService;
    @Resource
    private ISchoolService schoolService;
    @Resource
    private IPlanRelationClassesService relationClassesService;

    @Override
    public List<PlanStudent> listByPlanIds(List<Integer> planIds) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(PlanStudent::getPlanId, planIds);
        return list(wrapper);
    }

    @Override
    public Page<PlanStudent> getPageStudent(CommonPage<PlanStudent> pageParam) {
        PlanStudent query = pageParam.getQueryParam();
        QueryWrapper<PlanStudent>  wrapper = buildQueryWrapper(query);

        Page<PlanStudent> planStudentPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
        page(planStudentPage, wrapper);
        buildRecords(planStudentPage.getRecords());
        return planStudentPage;
    }

    private QueryWrapper<PlanStudent> buildQueryWrapper(PlanStudent query) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, query.getPlanId());

        if (StringUtils.hasLength(query.getStudyCode())) {
            wrapper.lambda().eq(PlanStudent::getStudyCode, query.getStudyCode());
        }
        if (StringUtils.hasLength(query.getSex())) {
            wrapper.lambda().eq(PlanStudent::getSex, query.getSex());
        }
        if (StringUtils.hasLength(query.getName())) {
            wrapper.lambda().like(PlanStudent::getName, query.getName());
        }
        if (query.getSchoolId() != null) {
            wrapper.lambda().eq(PlanStudent::getSchoolId, query.getSchoolId());
        }
        if (query.getGradeId() != null) {
            wrapper.lambda().eq(PlanStudent::getGradeId, query.getGradeId());
        }
        if (StringUtils.hasLength(query.getClassesCode())) {
            wrapper.lambda().eq(PlanStudent::getClassesCode, query.getClassesCode());
        }
        if (StringUtils.hasLength(query.getClassesName())) {
            wrapper.lambda().like(PlanStudent::getClassesName, query.getClassesName());
        }
        if (query.getFreeTestFlag() != null) {
            wrapper.lambda().eq(PlanStudent::getFreeTestFlag, query.getFreeTestFlag());
        }
        List<String> orders = new ArrayList<>();
        orders.add("grade_id");
        orders.add("classes_code");
        orders.add("study_code");
        wrapper.orderByAsc(orders);
        return wrapper;
    }

    @Override
    public List<PlanStudent> listStudents(PlanStudent studentParam) {
        QueryWrapper<PlanStudent>  wrapper = buildQueryWrapper(studentParam);
        List<PlanStudent> planStudents = list(wrapper);
        buildRecords(planStudents);
        return planStudents;
    }

    private void buildRecords(List<PlanStudent> records) {
        if (!CollectionUtils.isEmpty(records)) {
            Set<Integer> schoolIds = new HashSet<>();
            records.forEach(rec -> {
                schoolIds.add(rec.getSchoolId());
            });
            List<School> schoolList = schoolService.listByIds(schoolIds);
            Map<Integer, School> schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, s -> s));

            records.forEach(rec -> {
                rec.setSchoolName(schoolMap.getOrDefault(rec.getSchoolId(), new School()).getName());
                rec.setGradeName(GradeUtil.getGradeName(rec.getGradeId()));
            });
        }
    }

    /**
     * 批量处理列表数据
     * @param list 需要处理的列表
     * @param batchSize 批次大小
     * @param batchProcessor 批处理器
     * @param <T> 列表元素类型
     */
    private <T> void processByBatch(List<T> list, int batchSize, Consumer<List<T>> batchProcessor) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (int i = 0; i < list.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, list.size());
            List<T> batch = list.subList(i, endIndex);
            batchProcessor.accept(batch);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchUpdateStudentData(Integer planId) {
        // 1. 获取计划相关的学生和班级关系数据
        List<PlanStudent> planStudents = listByPlanId(planId);
        Map<String,PlanStudent> planStudentMap = planStudents.stream()
                .collect(Collectors.toMap(PlanStudent::getStudyCode, s -> s));
        
        List<PlanRelationClasses> planRelationClassesList = relationClassesService.listByPlanId(planId);
        Set<Integer> schoolIds = new HashSet<>();
        Set<Integer> existClassesIds = new HashSet<>();

        planRelationClassesList.forEach(pc -> {
            schoolIds.add(pc.getShcoolId());
            existClassesIds.add(pc.getClassesId());
        });

        // 2. 获取学生数据
        List<Student> studentList = studentService.listBySchoolIds(schoolIds);
        
        // 3. 使用并发处理来构建需要插入的数据
        List<PlanRelationClasses> insertPlanRelationClassesList = Collections.synchronizedList(new ArrayList<>());
        List<PlanStudent> insertPlanStudentList = Collections.synchronizedList(new ArrayList<>());

        // 使用并行流处理学生数据
        studentList.parallelStream().forEach(student -> {
            // 处理班级关系
            if(!existClassesIds.contains(student.getClassesId())){
                PlanRelationClasses planRelationClasses = new PlanRelationClasses();
                planRelationClasses.setClassesId(student.getClassesId());
                planRelationClasses.setGradeId(student.getGradeId());
                planRelationClasses.setShcoolId(student.getSchoolId());
                planRelationClasses.setPlanId(planId);
                insertPlanRelationClassesList.add(planRelationClasses);
            }

            // 处理学生数据
            PlanStudent planStudent = planStudentMap.get(student.getStudyCode());
            if(planStudent != null){
                Integer planStudentId = planStudent.getId();
                BeanUtils.copyProperties(student, planStudent);
                planStudent.setId(planStudentId);
                insertPlanStudentList.add(planStudent);
            } else {
                PlanStudent ps = new PlanStudent();
                BeanUtils.copyProperties(student, ps);
                ps.setPlanId(planId);
                ps.setId(null);
                insertPlanStudentList.add(ps);
            }
        });

        // 4. 批量保存数据，使用分批处理来避免一次性处理太多数据
        final int batchSize = 500;
        processByBatch(insertPlanRelationClassesList, batchSize, 
            batch -> relationClassesService.saveBatch(batch, batchSize));
        
        processByBatch(insertPlanStudentList, batchSize, 
            batch -> saveOrUpdateBatch(batch, batchSize));

        return true;
    }

    @Override
    public List<PlanStudent> listByPlanIdAndSchoolId(Integer planId, Integer schoolId) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        wrapper.lambda().eq(PlanStudent::getSchoolId, schoolId);
        return list(wrapper);
    }

    @Override
    public List<PlanStudent> listByPlanId(Integer planId) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        return list(wrapper);
    }

    @Override
    public void importBatchFreeTest(List<FreeTestImportModel> cachedDataList, Integer planId) {

        if (!CollectionUtils.isEmpty(cachedDataList)) {

            List<String> studyCodes = cachedDataList.stream().map(FreeTestImportModel::getStudyCode).collect(Collectors.toList());
            List<PlanStudent> students = queryByPlanIdAndStudyCodes(planId, studyCodes);

            students.forEach(planStudent -> cachedDataList.forEach(freeTestModel -> {
                if (planStudent.getStudyCode().equals(freeTestModel.getStudyCode())) {
                    planStudent.setFreeTestFlag(true);
                    planStudent.setFreeTestReason(freeTestModel.getFreeTestReason());
                }
            }));
            if (!CollectionUtils.isEmpty(students)) {
                updateBatchById(students);
            }
        }

    }

    @Override
    public List<PlanStudent> queryByPlanIdAndStudyCodes(Integer planId, Collection<String> studyCodes) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(PlanStudent::getStudyCode, studyCodes);
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        List<PlanStudent> list = list(wrapper);
        list.forEach(item -> {
            item.setGradeName(GradeUtil.getGradeName(item.getGradeId()));
        });
        return list;
    }

    @Override
    public long countByPlanId(PlanQueryParam planQueryParam) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, planQueryParam.getPlanId());
        if (planQueryParam.getSchoolId() != null) {
            wrapper.lambda().eq(PlanStudent::getSchoolId, planQueryParam.getSchoolId());
        }
        if (planQueryParam.getGradeId() != null) {
            wrapper.lambda().eq(PlanStudent::getGradeId, planQueryParam.getGradeId());
        }
        return count(wrapper);
    }

    @Override
    public boolean removeByPlanId(Integer planId) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        return remove(wrapper);
    }

    @Override
    public long listFreeTestByPlanIdAndSchoolId(Integer planId, Integer schoolId) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        wrapper.lambda().eq(PlanStudent::getSchoolId, schoolId);
        wrapper.lambda().eq(PlanStudent::getFreeTestFlag, 1);
        return count(wrapper);
    }

    @Override
    public List<PlanStudent> listForCountByPlanIdAndSchoolId(Integer planId, Integer schoolId) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().select(PlanStudent::getGradeId, PlanStudent::getFreeTestFlag, PlanStudent::getStudyCode, PlanStudent::getSex,PlanStudent::getClassesName);
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        wrapper.lambda().eq(PlanStudent::getSchoolId, schoolId);
        return list(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAvatar(String studyCode, String url) {
        List<PlanStudent> planStudents = queryByStudyCode(studyCode);
        if (!CollectionUtils.isEmpty(planStudents)) {
            planStudents.forEach(ps -> ps.setAvatarUrl(url));
            saveOrUpdateBatch(planStudents);
        }
        Student student = studentService.getByStudyCode(studyCode);
        if (student != null) {
            student.setAvatarUrl(url);
            studentService.updateById(student);
        }
    }

    @Override
    public List<PlanStudent> listByParam(PlanStudent query) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, query.getPlanId());

        if (StringUtils.hasLength(query.getStudyCode())) {
            wrapper.lambda().eq(PlanStudent::getStudyCode, query.getStudyCode());
        }
        if (StringUtils.hasLength(query.getSex())) {
            wrapper.lambda().eq(PlanStudent::getSex, query.getSex());
        }
        if (StringUtils.hasLength(query.getName())) {
            wrapper.lambda().like(PlanStudent::getName, query.getName());
        }
        if (query.getSchoolId() != null) {
            wrapper.lambda().eq(PlanStudent::getSchoolId, query.getSchoolId());
        }
        if (query.getGradeId() != null) {
            wrapper.lambda().eq(PlanStudent::getGradeId, query.getGradeId());
        }
        if (StringUtils.hasLength(query.getClassesName())) {
            wrapper.lambda().like(PlanStudent::getClassesName, query.getClassesName());
        }
        if (query.getFreeTestFlag() != null) {
            wrapper.lambda().eq(PlanStudent::getFreeTestFlag, query.getFreeTestFlag());
        }
        List<String> orders = new ArrayList<>();
        orders.add("grade_id");
        orders.add("classes_code");
        orders.add("study_code");
        wrapper.orderByAsc(orders);

        List<PlanStudent> studentList = list(wrapper);
        buildRecords(studentList);
        return studentList;
    }

    @Override
    public List<PlanStudent> listByPlanIdAndClassesNames(Integer planId, Set<String> classesNames) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        wrapper.lambda().in(PlanStudent::getClassesName, classesNames);
        return list(wrapper);
    }

    @Override
    public void updateStudentInfo(Integer planId) {
        this.baseMapper.updateStudentInfo(planId);
    }

    @Override
    public List<PlanStudent> listByStudyCodes(String studyCode) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getStudyCode, studyCode);
        return list(wrapper);
    }

    @Override
    public PlanStudent queryByPlanIdAndStudyCode(String studyCode, Integer planId) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getStudyCode, studyCode);
        wrapper.lambda().eq(PlanStudent::getPlanId, planId);
        List<PlanStudent> list = list(wrapper);
        list.forEach(item -> {
            item.setGradeName(GradeUtil.getGradeName(item.getGradeId()));
        });
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public List<PlanStudent> listByStudyCode(String studyCode) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getStudyCode, studyCode);
        return list(wrapper);
    }

    @Override
    public boolean removeByStudyCodes(Collection<String> studyCodes) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getStudyCode, studyCodes);
        return remove(wrapper);
    }


    private List<PlanStudent> queryByStudyCode(String studyCode) {
        QueryWrapper<PlanStudent> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(PlanStudent::getStudyCode, studyCode);
        return list(wrapper);
    }


}
